﻿using ChessGame.Movement;
using ChessGame.Pieces;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace ChessGame
{
    /// <summary>
    /// Game object
    /// </summary>
    public class Game
    {
        private Set white;
        private Set black;
        private Chess form;
        private Boolean turn;
        //private bool blackIsCheck;
        //private bool whiteIsCheck;

        /// <summary>
        /// Turn 
        /// </summary>
        /// <returns>True = Wit
        ///          False= Black</returns>
        public String Turn
        {
            get { if (turn) { return "White"; } else { return "Black"; } }
        }

        /// <summary>
        /// Game constructor
        /// </summary>
        /// <param name="_form">Linked form</param>
        public Game(Chess _form)
        {

            white = new Set(_form, this, "White");
            black = new Set(_form, this, "Black");
            form = _form;
        }

        /// <summary>
        /// Start's the game
        /// </summary>
        public void Start()
        {
            white.Start();
            black.Start();
            turn = true;
        }

        /// <summary>
        /// Stop's the game
        /// </summary>
        public void Stop()
        {
            white.Stop();
            black.Stop();

        }

        /// <summary>
        /// Next turn
        /// </summary>
        public void NextTurn()
        {

            if (turn) { turn = false; } else { turn = true; }
        }

        /// <summary>
        /// Move chesspiece to certain coordinate
        /// </summary>
        /// <param name="startX">Chesspiece to move position X</param>
        /// <param name="startY">Chesspiece to move position Y</param>
        /// <param name="endX">Move to position X</param>
        /// <param name="endY">Move to position Y</param>
        public void Move(int startX, int startY, int endX, int endY)
        {
            GetSet(Turn).Move(startX, startY, endX, endY);

            if (turn) { turn = false; } else { turn = true; }
            Console.WriteLine("Changed to" + Turn);
        }

        /// <summary>
        /// Get all the movement possibilities 
        /// </summary>
        /// <param name="colour">Colour of the side</param>
        /// <param name="x">Position X</param>
        /// <param name="y">Position Y</param>
        /// <returns>List of the MovementOption</returns>
        public List<MovementOption> GetMovementPossiblities(string colour, int x, int y)
        {
            List<MovementOption> listOptions, returnList;
            returnList = new List<MovementOption>();
            int newCoordinaatX, newCoordinaatY;
            listOptions = GetSet(colour).GetMovementPossiblities(x, y);

            foreach (MovementOption movementOption in listOptions)
            {
                // foreach movement Option get the new coordinaat
                newCoordinaatX = x + movementOption.X;
                newCoordinaatY = y + movementOption.Y;
                if ((newCoordinaatX <= 8) && (newCoordinaatY <= 8) && (newCoordinaatX >= 1) && (newCoordinaatY >= 1))
                {
                    if (!CheckBothSidesForPieces(newCoordinaatX, newCoordinaatY))
                    {
                        // Check if there's a own colour piece
                        if (!CheckIfBlocked(colour, x, y, newCoordinaatX, newCoordinaatY))
                        {
                            returnList.Add(movementOption);
                        }
                    }
                }
            }
            return returnList;
        }

        /// <summary>
        /// Get all the movement possibilities 
        /// TODO: Turn
        /// </summary>
        /// <param name="x">Position X</param>
        /// <param name="y">Position Y</param>
        /// <returns>List of the MovementOption</returns
        public List<MovementOption> GetMovementPossibilities(int x, int y)
        {
            List<MovementOption> listOptions, returnList;
            returnList = new List<MovementOption>();
            int newCoordinaatX, newCoordinaatY;

            listOptions = GetSet(Turn).GetMovementPossiblities(x, y);

            foreach (MovementOption movementOption in listOptions)
            {
                // foreach movement Option get the new coordinaat
                newCoordinaatX = x + movementOption.X;
                newCoordinaatY = y + movementOption.Y;
                if ((newCoordinaatX <= 8) && (newCoordinaatY <= 8) && (newCoordinaatX >= 1) && (newCoordinaatY >= 1))
                {
                    if (!CheckBothSidesForPieces(newCoordinaatX, newCoordinaatY))
                    {
                        // Check if there's a own colour piece
                        if (!CheckIfBlocked(Turn, x, y, newCoordinaatX, newCoordinaatY))
                        {
                            returnList.Add(movementOption);
                        }
                    }
                }
            }
            return returnList;
        }

        /// <summary>
        /// Get all the hit possibilities
        /// </summary>
        /// <param name="colour">Colour of the side</param>
        /// <param name="x">Position X</param>
        /// <param name="y">Position Y</param>
        /// <returns>List of the HitOption</returns>
        public List<HitOption> GetHitPossiblities(string colour, int x, int y)
        {
            int newCoordinaatX, newCoordinaatY;
            List<HitOption> returnList = new List<HitOption>();
            List<HitOption> listOptions = GetSet(colour).GetHitPossiblities(x, y);

            foreach (HitOption hitOption in listOptions)
            {
                // foreach movement Option get the new coordinaat
                newCoordinaatX = x + hitOption.X;
                newCoordinaatY = y + hitOption.Y;
                if ((newCoordinaatX <= 8) && (newCoordinaatY <= 8) && (newCoordinaatX >= 1) && (newCoordinaatY >= 1))
                {
                    if (CheckOtherSideForPieces(colour, newCoordinaatX, newCoordinaatY))
                    {
                        // Check if there's a own colour piece
                        if (!CheckIfBlocked(colour, x, y, newCoordinaatX, newCoordinaatY))
                        {
                            returnList.Add(hitOption);
                        }
                    }
                }
            }
            return returnList;
        }

        /// <summary>
        /// Get all the hit possibilities
        /// </summary>
        /// <param name="x">Position X</param>
        /// <param name="y">Position Y</param>
        /// <returns>List of the HitOption</returns>
        public List<HitOption> GetHitPossiblities(int x, int y)
        {

            //List<HitOption> listOptions, returnList;
            int newCoordinaatX, newCoordinaatY;
            List<HitOption> returnList = new List<HitOption>();
            List<HitOption> listOptions = GetSet(Turn).GetHitPossiblities(x, y);

            foreach (HitOption hitOption in listOptions)
            {
                // foreach movement Option get the new coordinaat
                newCoordinaatX = x + hitOption.X;
                newCoordinaatY = y + hitOption.Y;
                if ((newCoordinaatX <= 8) && (newCoordinaatY <= 8) && (newCoordinaatX >= 1) && (newCoordinaatY >= 1))
                {
                    if (CheckOtherSideForPieces(Turn, newCoordinaatX, newCoordinaatY))
                    {
                        // Check if there's a own colour piece
                        if (!CheckIfBlocked(Turn, x, y, newCoordinaatX, newCoordinaatY))
                        {
                            returnList.Add(hitOption);
                        }
                    }
                }
            }
            return returnList;
        }

        /// <summary>
        /// Check's both sides if there's a piece on a certain place.
        /// </summary>
        /// <param name="x">Position X</param>
        /// <param name="y">Position Y</param>
        /// <returns>Boolean if there's a piece</returns>
        public bool CheckBothSidesForPieces(int x, int y)
        {
            if (white.CheckForPiece(x, y))
            {
                return true;
            }
            else if (black.CheckForPiece(x, y))
            {
                return true;
            }
            else
            {
                return false;
            }
        }

        /// <summary>
        /// Check's otherside if there's a piece on a certain place
        /// </summary>
        /// <param name="colour">Colour of the side</param>
        /// <param name="x">Position X</param>
        /// <param name="y">Position Y</param>
        /// <returns>Boolean if there's a piece</returns>
        public bool CheckOtherSideForPieces(string colour, int x, int y)
        {
            if (GetOpponentSet(colour).CheckForPiece(x, y))
            {
                return true;
            }
            return false;
        }

        /// <summary>
        /// Check's own side if there's a piece on a certain place
        /// </summary>
        /// <param name="x">Position X</param>
        /// <param name="y">Position Y</param>
        /// <returns>Boolean if there's a piece</returns>
        public bool CheckOwnSideForPiece(int x, int y)
        {
            return GetSet(Turn).CheckForPiece(x, y);
        }

        /// <summary>
        /// Check's if a there's a block between certain coordinates
        /// </summary>
        /// <param name="colour">Colour of side to check</param>
        /// <param name="baseObjectX">From position X</param>
        /// <param name="baseObjectY">From position Y</param>
        /// <param name="endPointX">To position X</param>
        /// <param name="endPointY">To position Y</param>
        /// <returns>True=Chesspiece between the coordinates</returns>
        /// <returns>boolean if blocked</returns>
        public bool CheckIfBlocked(string colour, int baseObjectX, int baseObjectY, int endPointX, int endPointY)
        {
            return GetSet(colour).CheckIfBlocked(baseObjectX, baseObjectY, endPointX, endPointY);
        }

        /// <summary>
        /// Kill a chesspiece
        /// </summary>
        /// <param name="coordinaatX">Position X</param>
        /// <param name="coordinaatY">Position Y</param>
        public void Kill(int coordinaatX, int coordinaatY)
        {
            GetOpponentSet(Turn).Kill(coordinaatX, coordinaatY);
        }

        /// <summary>
        /// Set enemy checked
        /// </summary>
        //public void Check()
        //{
        //    if (Turn == "White")
        //    {
        //        whiteIsCheck = true;
        //    }
        //    else if (Turn == "Black")
        //    {
        //        blackIsCheck = true;
        //    }
        //}

        /// <summary>
        /// Check's if your check
        /// </summary>
        /// <returns>True if your check</returns>
        public Boolean CheckIfCheck(string colour)
        {
            int newX, newY;
            int x, y;

            Chesspiece king = GetSet(colour).GetKing();
            x = king.PositionX;
            y = king.PositionY;
            foreach (Chesspiece chesspiece in GetOpponentSet(colour).GetPieces())
            {
                List<HitOption> listHitOptions = GetHitPossiblities(GetOpponentColour(colour), chesspiece.PositionX, chesspiece.PositionY);
                foreach (HitOption hitoption in listHitOptions)
                {
                    newX = chesspiece.PositionX + hitoption.X;
                    newY = chesspiece.PositionY + hitoption.Y;
                    if ((x == newX) && (y == newY))
                    {
                        return true;
                    }
                }
            }
            return false;
        }

        /// <summary>
        /// Check if your checkmated
        /// </summary>
        /// <returns>boolean </returns>
        public Boolean CheckIfCheckMate(string colour)
        {
            int newKingX, newKingY;
            int newX, newY;
            int x, y;

            Chesspiece king = GetSet(colour).GetKing();
            x = king.PositionX;
            y = king.PositionY;
            List<MovementOption> lMovementOption = GetMovementPossiblities(colour, x, y);
            int nCount = lMovementOption.Count();
            foreach (MovementOption movementOption in lMovementOption)
            {
                newKingX = x + movementOption.X;
                newKingY = y + movementOption.Y;
                foreach (Chesspiece chesspiece in GetOpponentSet(colour).GetPieces())
                {
                    try
                    {
                        List<MovementOption> lListMovementOptions = GetMovementPossiblities(GetOpponentColour(colour), chesspiece.PositionX, chesspiece.PositionY);
                        foreach (MovementOption movementoption in lListMovementOptions)
                        {
                            newX = chesspiece.PositionX + movementoption.X;
                            newY = chesspiece.PositionY + movementoption.Y;
                            if ((newKingX == newX) && (newKingY == newY))
                            {
                                nCount--;
                                if (nCount == 0)
                                {
                                    if (CheckIfOtherPieces(x, y))
                                    {
                                        return false;
                                    }
                                    return true;
                                }
                            }
                        }
                    }
                    catch (Exception)
                    {
                    }

                }
            }

            return false;
        }

        /// <summary>
        /// Check if other pieces
        /// </summary>
        /// <param name="kingX">your king x</param>
        /// <param name="kingY">your king y</param>
        /// <returns></returns>
        public Boolean CheckIfOtherPieces(int kingX, int kingY)
        {

            List<Chesspiece> lChekPieces = GetCheckPieces(GetOpponentColour(Turn));
            foreach (Chesspiece chekpieces in lChekPieces)
            {
                foreach (Chesspiece chesspiece in GetSet(Turn).GetPieces())
                {
                    List<MovementOption> lMovementOptions = GetMovementPossiblities(Turn, chesspiece.PositionX, chesspiece.PositionY);
                    foreach (MovementOption movement in lMovementOptions)
                    {
                        int vanaf;
                        int naar;
                        if (kingX < chekpieces.PositionX && kingY == chekpieces.PositionY)
                        {
                            vanaf = kingX;
                            naar = chekpieces.PositionX;
                        }
                        else if (kingY < chekpieces.PositionY && kingX == chekpieces.PositionX)
                        {
                            vanaf = kingY;
                            naar = chekpieces.PositionY;
                        }
                        else if (kingY > chekpieces.PositionY && kingX == chekpieces.PositionX)
                        {
                            vanaf = chekpieces.PositionY;
                            naar = kingY;
                        }
                        else if (kingX > chekpieces.PositionX && kingY == chekpieces.PositionY)
                        {
                            vanaf = chekpieces.PositionX;
                            naar = kingX;
                        }
                        else
                        {
                            vanaf = 0;
                            naar = 0;
                        }
                        for (int i = vanaf; i > naar; i++)
                        {
                            if (movement.X == i && movement.Y == kingY)
                            {
                                return true;
                            }
                        }
                    }
                }
            }

            return false;
        }

        /// <summary>
        /// Random Computer move.
        /// </summary>
        public void RandomComputerMove()
        {
            Random randomizer = new Random();
            Chesspiece chesspiece;
            string colour;
            if (Turn == "White")
            {
                colour = "White";
                chesspiece = white.RandomChesspiece();
            }
            else if (Turn == "Black")
            {
                colour = "Black";
                chesspiece = black.RandomChesspiece();
            }
            else
            {
                chesspiece = new Chesspiece();
                throw new Exception("kanniet");
            }
            List<MovementOption> listMovement = GetMovementPossibilities(chesspiece.PositionX, chesspiece.PositionY);
            List<HitOption> listHit = GetHitPossiblities(colour, chesspiece.PositionX, chesspiece.PositionY);
            if ((listMovement.Count == 0) && (listHit.Count == 0))
            {
                RandomComputerMove();
            }
            else
            {
                if (listHit.Count == 0)
                {
                    int random = randomizer.Next(0, listMovement.Count);
                    MovementOption movementOption = listMovement[random];
                    Move(chesspiece.PositionX, chesspiece.positionY, (chesspiece.positionX + movementOption.X), (chesspiece.positionY + movementOption.Y));
                }
                else
                {
                    int random = randomizer.Next(0, listHit.Count);
                    HitOption hitOption = listHit[random];
                    Kill((chesspiece.positionX + hitOption.X), (chesspiece.positionY + hitOption.Y));
                    Move(chesspiece.PositionX, chesspiece.positionY, (chesspiece.positionX + hitOption.X), (chesspiece.positionY + hitOption.Y));

                }
            }



        }

        /// <summary>
        /// Calculated Computer move.
        /// </summary>
        public void CalculatedMove()
        {
            /// Get all chesspieces
            List<Chesspiece> whiteChesspieces = white.GetAllChesspieces();
            List<Chesspiece> blackChesspieces = black.GetAllChesspieces();

            //Chesspiece enemyValue;
            Random randomizer = new Random();
            List<AIMovement> aiMovementList = GetAIMovementList("Black", blackChesspieces, whiteChesspieces);

            //Calculate best move
            AIMovement aiMovement               = aiMovementList.OrderByDescending(item => item.MovementScore()).First();
            List<AIMovement> aiBestMovementList = aiMovementList.Where(item => item.MovementScore() == aiMovement.MovementScore()).ToList();

            // If more movements with same score.
            if (aiBestMovementList.Count > 1)
            {
                int random = randomizer.Next(0, aiBestMovementList.Count);
                aiMovement = aiBestMovementList[random];
            }

            // If best move is hit or move
            if (aiMovement.hit)
            {
                Kill(aiMovement.enemypiece.PositionX, aiMovement.enemypiece.PositionY);
                Move(aiMovement.chesspiece.PositionX, aiMovement.chesspiece.PositionY, aiMovement.enemypiece.PositionX, aiMovement.enemypiece.PositionY);
            }
            else
            {
                Move(aiMovement.chesspiece.PositionX, aiMovement.chesspiece.PositionY, aiMovement.EndPositionX, aiMovement.EndPositionY);
            }

        }

        /// <summary>
        /// Get list of All AIMovement for certain black and white pieces
        /// </summary>
        /// <param name="blackChesspieces">black Chesspieces</param>
        /// <param name="whitepieces">white Chesspiece</param>
        /// <returns>List Ai Movement</returns>
        public List<AIMovement> GetAIMovementList(string colour, List<Chesspiece> computerPieces, List<Chesspiece> enemyPieces)
        {

            Chesspiece enemyValue;
            // Create new AIMovement List
            List<AIMovement> aiMovementList = new List<AIMovement>();
            if (CheckIfCheck(colour))
            {
                // Move The King Options
                Chesspiece           king                = computerPieces.Where(c => c.Type == "King").ToList()[0];
                List<MovementOption> kingMovementOptions = GetMovementPossibilities(king.PositionX, king.PositionY);
                List<HitOption>      kingHitOptions      = GetHitPossiblities(king.PositionX, king.PositionY);

                // Let king hit a chesspiece
                foreach (HitOption hitOption in kingHitOptions)
                {
                    enemyValue = white.GetPiece((king.positionX + hitOption.X), (king.positionY + hitOption.Y));
                    aiMovementList.Add(new AIMovement(true, king, enemyValue));
                }

                // Move king to other position 
                // TODO: Must check if it's safe
                foreach (MovementOption movementOption in kingMovementOptions)
                {
                    aiMovementList.Add(new AIMovement(false, king, movementOption));
                }
                // Move Piece in front of the King
                foreach (Chesspiece checkPiece in GetCheckPieces(colour))
                {
                    //TODO: Calculate chesspiees between
                }
            }
            else
            {
                List<HitOption> hitOptions;
                List<MovementOption> movementOptions;
                foreach (Chesspiece chesspiece in computerPieces)
                {
                    // Get's all options of the chesspiece.
                    hitOptions = GetHitPossiblities(Turn, chesspiece.PositionX, chesspiece.PositionY);
                    movementOptions = GetMovementPossiblities(Turn, chesspiece.PositionX, chesspiece.PositionY);

                    // Add all hit options in AIMovement list.
                    foreach (HitOption hitOption in hitOptions)
                    {
                        enemyValue = white.GetPiece((chesspiece.PositionX + hitOption.X), (chesspiece.positionY + hitOption.Y));
                        aiMovementList.Add(new AIMovement(true, chesspiece, enemyValue));
                    }
                    // Add all movement options in AIMovement list.
                    foreach (MovementOption movementOption in movementOptions)
                    {
                        aiMovementList.Add(new AIMovement(false, chesspiece, movementOption));
                    }
                }
            }
            return aiMovementList;
        }

        /// <summary>
        /// Get's all the pieces that sets the king check.
        /// </summary>
        /// <returns></returns>
        public List<Chesspiece> GetCheckPieces(string colour)
        {
            List<Chesspiece> returnList = new List<Chesspiece>();
            Chesspiece king = GetSet(colour).GetKing();
            Set enemySet = GetOpponentSet(colour);

            foreach (Chesspiece chesspiece in enemySet.GetAllChesspieces())
            {
                if (CompareIfHit(chesspiece, king))
                {
                    returnList.Add(chesspiece);
                }
            }
            return returnList;

        }

        /// <summary>
        /// Compare If Hit
        /// </summary>
        /// <param name="chesspieceSelected"></param>
        /// <param name="chesspieceToHit"></param>
        /// <returns></returns>
        public bool CompareIfHit(Chesspiece chesspieceSelected, Chesspiece chesspieceToHit)
        {
            int newX, newY;

            foreach (HitOption hitOption in chesspieceSelected.GetHitPossibilities())
            {
                newX = chesspieceSelected.PositionX + hitOption.X;
                newY = chesspieceSelected.PositionY + hitOption.Y;
                if ((chesspieceToHit.PositionX == newX) && (chesspieceToHit.PositionY == newY))
                {
                    return true;
                }
            }

            return false;
        }

        /// <summary>
        /// Print All Pieces
        /// </summary>
        public void PrintAll()
        {
            white.PrintPieces();
            black.PrintPieces();
        }

        /// <summary>
        /// Get Set By Colour
        /// </summary>
        /// <param name="colour">Set Colour</param>
        /// <returns></returns>
        public Set GetSet(string colour)
        {
            if (colour == "White")
            {
                return white;
            }
            else if (colour == "Black")
            {
                return black;
            }
            else
            {
                throw new Exception("No used colour");
            }
        }

        /// <summary>
        /// Get's opponent colour
        /// </summary>
        /// <param name="colour">your colour</param>
        /// <returns></returns>
        public string GetOpponentColour(string colour)
        {
            if (colour == "White")
            {
                return "Black";
            }
            else if (colour == "Black")
            {
                return "White";
            }
            else
            {
                throw new Exception("No used colour");
            }
        }

        /// <summary>
        /// Get Opponent Set
        /// </summary>
        /// <param name="colour">Your Colour</param>
        /// <returns>There Set</returns>
        public Set GetOpponentSet(string colour)
        {
            if (colour == "White")
            {
                return black;
            }
            else if (colour == "Black")
            {
                return white;
            }
            else
            {
                throw new Exception("No used colour");
            }
        }

    }
}
